﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace TableTopWidgets
{
    /// <summary>
    /// Interaction logic for RadialButton.xaml
    /// </summary>
    public class RadialButton : Button
    {
        #region _height
        double _height
        {
            get { return (double)GetValue(heightProperty); }
            set { SetValue(heightProperty, value); }
        }
        static readonly DependencyProperty heightProperty =
            DependencyProperty.Register(
            "_height",
            typeof(double),
            typeof(RadialButton));
        #endregion
        #region _width
        double _width
        {
            get { return (double)GetValue(widthProperty); }
            set { SetValue(widthProperty, value); }
        }
        static readonly DependencyProperty widthProperty =
            DependencyProperty.Register(
            "_width",
            typeof(double),
            typeof(RadialButton));
        #endregion
        #region _borderGeometry
        Geometry _borderGeometry
        {
            get { return (Geometry)GetValue(_borderGeometryProperty); }
            set { SetValue(_borderGeometryProperty, value); }
        }
        static readonly DependencyProperty _borderGeometryProperty =
            DependencyProperty.Register(
            "_borderGeometry",
            typeof(Geometry),
            typeof(RadialButton));
        #endregion
        #region TextMargin
        public Thickness TextMargin
        {
            get { return (Thickness)GetValue(TextMarginProperty); }
            set { SetValue(TextMarginProperty, value); }
        }
        public static readonly DependencyProperty TextMarginProperty =
            DependencyProperty.Register(
                "TextMargin",
                typeof(Thickness),
                typeof(RadialButton),
                new FrameworkPropertyMetadata(new Thickness(),
                    FrameworkPropertyMetadataOptions.AffectsRender));
        #endregion

        #region InnerRadius
        private const double DEFAULT_INNER_RADIUS = 0;
        public double InnerRadius
        {
            get { return (double)GetValue(InnerRadiusProperty); }
            set { SetValue(InnerRadiusProperty, value); }
        }
        public static readonly DependencyProperty InnerRadiusProperty =
            DependencyProperty.Register(
                "InnerRadius",
                typeof(double),
                typeof(RadialButton),
                new FrameworkPropertyMetadata(DEFAULT_INNER_RADIUS,
                    FrameworkPropertyMetadataOptions.AffectsParentMeasure, innerRadiusChangedCallback));
        static void innerRadiusChangedCallback(DependencyObject target, DependencyPropertyChangedEventArgs e)
        {
            RadialButton button = (RadialButton)target;
            button.isInnerRadiusDefault = (double)e.NewValue == DEFAULT_INNER_RADIUS;

            button.UpdateGeometry();
            button.InvalidateVisual();
        }
        #endregion
        #region OuterRadius
        private const double DEFAULT_OUTER_RADIUS = 0;
        public double OuterRadius
        {
            get { return (double)GetValue(OuterRadiusProperty); }
            set { SetValue(OuterRadiusProperty, value); }
        }
        public static readonly DependencyProperty OuterRadiusProperty =
            DependencyProperty.Register(
                "OuterRadius",
                typeof(double),
                typeof(RadialButton),
                new FrameworkPropertyMetadata(DEFAULT_OUTER_RADIUS,
                    FrameworkPropertyMetadataOptions.AffectsParentMeasure, outerChangedRadiusCallback));
        static void outerChangedRadiusCallback(DependencyObject target, DependencyPropertyChangedEventArgs e)
        {
            RadialButton button = (RadialButton)target;
            button.isOuterRadiusDefault = (double)e.NewValue == DEFAULT_OUTER_RADIUS;

            button.UpdateGeometry();
            button.InvalidateVisual();
        }
        #endregion
        #region Angle
        private const double DEFAULT_ANGLE = 0;
        public double Angle
        {
            get { return (double)GetValue(AngleProperty); }
            set { SetValue(AngleProperty, value); }
        }
        public static readonly DependencyProperty AngleProperty =
            DependencyProperty.Register(
                "Angle",
                typeof(double),
                typeof(RadialButton),
                new FrameworkPropertyMetadata(DEFAULT_ANGLE,
                    FrameworkPropertyMetadataOptions.AffectsParentMeasure, angleChangedCallback));
        static void angleChangedCallback(DependencyObject target, DependencyPropertyChangedEventArgs e)
        {
            RadialButton button = (RadialButton)target;
            button.isAngleDefault = (double)e.NewValue == DEFAULT_ANGLE;

            button.UpdateGeometry();
            button.InvalidateVisual();
        }
        #endregion
        #region Thickness
        private const double DEFAULT_THICKNESS = 1;
        public double Thickness
        {
            get { return (double)GetValue(ThicknessProperty); }
            set { SetValue(ThicknessProperty, value); }
        }
        public static readonly DependencyProperty ThicknessProperty =
            DependencyProperty.Register(
                "Thickness",
                typeof(double),
                typeof(RadialButton),
                new FrameworkPropertyMetadata(DEFAULT_THICKNESS,
                    FrameworkPropertyMetadataOptions.AffectsRender));
        #endregion

        public bool isAngleDefault
        {
            get;
            protected set;
        }
        public bool isOuterRadiusDefault
        {
            get;
            protected set;
        }
        public bool isInnerRadiusDefault
        {
            get;
            protected set;
        }

        static RadialButton()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(RadialButton), new FrameworkPropertyMetadata(typeof(RadialButton)));
        }

        public RadialButton()
        {
            isAngleDefault = true;
            isOuterRadiusDefault = true;
            isInnerRadiusDefault = true;

            SetValue(_borderGeometryProperty, GetGeometry());
        }
        private Geometry GetGeometry()
        {
            //Calculate the parameters of the button
            string geometryString = string.Empty;
            _height = GetHeight();
            _width = GetWidth();

            
            int isLargeArc = Angle <= 180 ? 0 : 1;
            double radAngle = Angle * Math.PI / (180);
            double cos = Math.Cos(radAngle / 2);
            double sin = Math.Sin(radAngle / 2);

            double topArcHeight = OuterRadius - OuterRadius * cos;
            double topArcRightXCoord = Angle <= 180 ? _width : OuterRadius + Math.Abs(OuterRadius * sin);
            double topArcLeftXCoord = Angle <= 180 ? 0 : OuterRadius - Math.Abs(OuterRadius * sin);

            double bottomWidthMargin = _width / 2 - Math.Abs(InnerRadius * sin);
            double bottomRightXCoord = _width - bottomWidthMargin;
            double bottomArcYCoord = Angle <= 180 ? _height : _height + (OuterRadius - InnerRadius) * cos;
            if (Angle < 360)
            {
                //Create GeometryPath
                geometryString =
                    //Starting point
                    "M " + topArcLeftXCoord + "," + topArcHeight
                    //Left Side
                    + " L " + bottomWidthMargin + "," + bottomArcYCoord
                    //Bottom Arc
                    + " A " + InnerRadius + "," + InnerRadius + " 0.0 " + isLargeArc + " 1 " + bottomRightXCoord + "," + bottomArcYCoord
                    //Right Side
                    + " L " + topArcRightXCoord + "," + topArcHeight
                    //Top Arc
                    + " A " + OuterRadius + "," + OuterRadius + " 0.0 " + isLargeArc + " 0 " + topArcLeftXCoord + "," + topArcHeight;
            }
            if (Angle == 360)
            {
                if (InnerRadius == 0)
                    return new EllipseGeometry(new Point(_height / 2, _width / 2), OuterRadius, OuterRadius);
                else
                {
                    //create a geometry group or donut
                    GeometryGroup group = new GeometryGroup();
                    EllipseGeometry outsideBorder = new EllipseGeometry(new Point(_width / 2, _height / 2), OuterRadius, OuterRadius);
                    EllipseGeometry insideBorder = new EllipseGeometry(new Point(_width / 2, _height / 2), InnerRadius, InnerRadius);
                    group.Children.Add(outsideBorder);
                    group.Children.Add(insideBorder);

                    return group;
                }
            }
            return Geometry.Parse(geometryString);
        }
        public double GetHeight()
        {
            double radAngle = Angle * Math.PI / (180);
            double ret = 0;
            if (Angle <= 180)
                ret = (OuterRadius - InnerRadius * Math.Cos(radAngle / 2));
            else if (Angle >= 180 && Angle <= 360)
                ret = (OuterRadius - OuterRadius * Math.Cos(radAngle / 2));
            return ret;
        }
        public double GetWidth()
        {
            double radAngle = Angle * Math.PI / (180);
            double ret = Angle <= 180 ? 2 * OuterRadius * Math.Sin(radAngle / 2) : 2 * OuterRadius;
            return ret;
        }

        public void UpdateGeometry()
        {
            //Set the margin
            
            double distanceFromTop = (OuterRadius - InnerRadius) / 2;
            Thickness margin = new Thickness(0, distanceFromTop, 0, 0);
            SetValue(TextMarginProperty, margin);

            //Set the border
            SetValue(_borderGeometryProperty, GetGeometry());
        }
        public void SetAutoSizeAngle(double newAngle)
        {
            Angle = newAngle;
            isAngleDefault = true;
            UpdateGeometry();
            InvalidateVisual();
        }
        public void SetAutoSizeOuterRadius(double value)
        {
            OuterRadius = value;
            isOuterRadiusDefault = true;
        }
        public void SetAutoSizeInnerRadius(double value)
        {
            InnerRadius = value;
            isInnerRadiusDefault = true;
        }
        protected override void OnRender(DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);
            UpdateGeometry();
        }
    }
}
